using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
using System.Drawing.Imaging;
using System.Text;

public class MeshMerge
{
    public enum MeshMergeFlags
    {
        None = 0,
        PowerOfTwo = 1<<1,
        MergeSubUV = 1<<2,
        OptimizeUV = 1<<3,
        UseSingleLine = 1<<4,
        IncludeUV2 = 1<<5
    }

    public enum MeshMergeSpace
    {
        WorldSpace,
        LocalSpace
    }

    private int maxResWidth = 64;
    private int maxResHeight = 64;

    public string name {get;set;}
    private MeshMergeFlags flags = MeshMergeFlags.None;
           
    private List<SubRendererData> subRendererDatas = null;

    public Transform rootTransform = null;

    
    static Material drawTexMat = null;

    static RenderTexture tempRT = null;

    public RenderTexture mergeRT = null;

    public GameObject lastSelectObj = null;

    /// <summary>
    /// Unity目前支持最大分辨率
    /// </summary>
    public const int MAX_ITER_NUM = 16384;

    public List<Vector3> allVertices = new List<Vector3>();
    public List<Vector3> allNormals = new List<Vector3>();
    public List<Vector2> allUvs = new List<Vector2>();
    public List<Vector2> allUv2 = new List<Vector2>();
    public List<int> allTriangles = new List<int>();

    public MeshMergeSpace meshMergeSpace =  MeshMergeSpace.LocalSpace; 


    public MeshMerge(){
        subRendererDatas = new List<SubRendererData>();
    }

    
    public MeshMerge(MeshMergeFlags flags ){
        subRendererDatas = new List<SubRendererData>();
        this.flags =flags;
    }
    public static bool HasFlags(int value,MeshMergeFlags flags)
    {
        return ((int)flags & value)>0;
    }

    public void MergeProcess()
    {
        this.allVertices.Clear();   
        this.allNormals.Clear();   
        this.allUvs.Clear();   
        this.allTriangles.Clear();

       //优化UV
       if(HasFlags((int)flags,MeshMergeFlags.OptimizeUV))
       {
            OptimizeUV(subRendererDatas);
       }

       //合并相同贴图的模型
       
       if(HasFlags((int)flags,MeshMergeFlags.MergeSubUV))
       {
            MergeSubUV(subRendererDatas);
       }

       //计算面积与大小
       foreach(var data in subRendererDatas)
       {
            data.CalSize();
       }

       bool useSingleLine = HasFlags((int)flags,MeshMergeFlags.UseSingleLine);

       //排序
       subRendererDatas = subRendererDatas.OrderByDescending(n=>n.area).ToList();

       //计算分辨率
       int maxRes = 0;
       foreach(var data in subRendererDatas)
        {
            //判断是否有资格独享一行
            if(useSingleLine)
            if(data.refMainTexture.width>=512 && (data.maxUVValue.x-data.minUVValue.x)>3)
            {
                //你是贪心鬼，为什么独享一行！
                Debug.LogFormat("{0}独享一行",data.name);
                data.singleLineFlags = true;
                maxRes = Mathf.CeilToInt(Mathf.Max(maxRes,data.refMainTexture.width)); 
                continue;
            }
            maxRes = Mathf.CeilToInt(Mathf.Max(maxRes,data.width)); 
        }

       maxResWidth = GetNewResSize(maxRes);
       maxResHeight = 16;
       
       bool hasSingleLine  =false;
       //贪心鬼大小设置
       foreach(var data in subRendererDatas)
        {
            if(data.singleLineFlags)
            {
                data.width = maxResWidth;
                hasSingleLine =true;
            }
        }
       //放置贴图
       List<SubRendererData> placed  =new List<SubRendererData>();
       foreach(var data in subRendererDatas)
       {
            PlaceUV(data,placed);
       }

       if(placed.Count!=subRendererDatas.Count)
       {
            Debug.LogError("placed.Count!=subRendererDatas.Count 存在未放置的UV数据");
       }

       mergeRT = new RenderTexture(GetNewResSize(maxResWidth),GetNewResSize(maxResHeight),0);
       mergeRT.wrapMode = hasSingleLine? TextureWrapMode.Repeat : TextureWrapMode.Clamp;
   
       //合并贴图 并且求出UV
       MergeTexture(placed,allUvs);

        var indexOffset = 0;
        foreach(var data in placed)
        {
            indexOffset = allVertices.Count;

            allVertices.AddRange(data.vertices);
            allNormals.AddRange(data.normals);

         
            foreach(var tri in data.triangles)
            {
               allTriangles.Add(tri+indexOffset);
            }
        }

    }

    public static MeshMerge GenerateMeshMerge(GameObject root,MeshMergeFlags flags,MeshMergeSpace space)
    {  
        if(root ==null)
        {
            return null;
        }
        MeshMerge meshMerge = new MeshMerge(flags);
        meshMerge.meshMergeSpace = space;
    
        meshMerge.name = root.name;

       var renderers = root.GetComponentsInChildren<MeshRenderer>();

       Transform rootTransform = root.transform;

       meshMerge.rootTransform =rootTransform;

       var includeUV2 = HasFlags((int)flags,MeshMergeFlags.IncludeUV2);
       
       //提取网格数据
       foreach(var renderer in renderers)
       {
            var mesh = renderer.GetComponent<MeshFilter>().sharedMesh;

            int subMeshCount = mesh.subMeshCount;
            
            for(var subMeshIndex=0;subMeshIndex<subMeshCount;subMeshIndex++)
            {
                
                SubRendererData srd = new SubRendererData();
                var vertices = srd.vertices;
                var normals = srd.normals;
                var triangles =srd.triangles;
                
                var subMeshDesc = mesh.GetSubMesh(subMeshIndex);

                //网格数据
                var subVertices = mesh.vertices;
                var subTriangles = mesh.triangles;
                var subNormals = mesh.normals;
                var uv = mesh.uv;
                var uv2 = mesh.uv2;

                Matrix4x4 vertexMatrix;

                if(space==MeshMergeSpace.WorldSpace)
                {
                    vertexMatrix =renderer.localToWorldMatrix;
                } 
                else
                {
                    vertexMatrix = rootTransform.worldToLocalMatrix*renderer.transform.localToWorldMatrix;
                }
                //处理顶点和法线
                int indexStart = subMeshDesc.indexStart;
                int indexCount = subMeshDesc.indexCount;
                int vertexCount = subMeshDesc.vertexCount;
                int firstVertex =subMeshDesc.firstVertex;
                for(var index =indexStart;index<indexStart+indexCount;index++)
                {
                    triangles.Add(subTriangles[index] - firstVertex);
                }
                for(var index =firstVertex;index<firstVertex+vertexCount;index++)
                {
                    var v = vertexMatrix.MultiplyPoint(subVertices[index]);

                    var localToWorldMatrix = renderer.transform.localToWorldMatrix;
                    //部分法线出问题
                    var n =localToWorldMatrix.MultiplyVector(subNormals[index]);

                    vertices.Add(v);
                    normals.Add(n);
                }

                if(uv.Length==0)
                {
                    Debug.LogWarning("'{0}缺少UV数据！'");
                    uv = new Vector2[subVertices.Length];
                    for(var uidx=firstVertex;uidx<firstVertex+vertexCount;uidx++)
                        uv[uidx] = new Vector2();
                }
                for(var index =firstVertex;index<firstVertex+vertexCount;index++)
                {
                    srd.AddUV(uv[index]);
                }

                if(includeUV2)
                {
                    if(uv2.Length==0)
                    {
                        uv2 = new Vector2[subVertices.Length];
                        for(var uidx=firstVertex;uidx<firstVertex+vertexCount;uidx++)
                            uv2[uidx] = new Vector2();
                    }
                    for(var index =firstVertex;index<firstVertex+vertexCount;index++)
                    {
                        srd.uv2.Add(uv2[index]);
                    }
                }
                srd.name = renderer.name;
                srd.refMainTexture = renderer.sharedMaterials[subMeshIndex].mainTexture as Texture2D;
                srd.mainColor = renderer.sharedMaterials[subMeshIndex].color.linear;

                meshMerge.subRendererDatas.Add(srd);
            }
       }
       return meshMerge;
    }

    public static void OptimizeUV(List<SubRendererData> subRendererDatas)
    {
        foreach(var data in subRendererDatas)
       {
            data.OptimizeUV();
       }
    }
    public static void MergeSubUV(List<SubRendererData> subRendererDatas)
    {
        Dictionary<Texture,SubRendererData> srdDic = new Dictionary<Texture, SubRendererData>();
        //foreach(var srd in subMeshUVs )
        for(var idx=0;idx<subRendererDatas.Count;)
        {
                var srd = subRendererDatas[idx];
                if(srdDic.ContainsKey(srd.refMainTexture))
                {
                    srdDic[srd.refMainTexture].Merge(srd);
                    subRendererDatas.RemoveAt(idx);
                }
                else
                {
                    srdDic.Add(srd.refMainTexture,srd);
                    idx++;
                }
        }
    }    

    public void PlaceTexture(Color color,Texture texture,Vector2 position,Vector2 size,Vector4 tilingAndOffset)
    {
        if(texture==null)
        {
            Debug.Log("Texture Is Null!");
            return;
        }
        if(tempRT==null || tempRT.width!=mergeRT.width || tempRT.height!=mergeRT.height)
        {
            if(tempRT!=null) tempRT.Release();
            tempRT = new RenderTexture(mergeRT.width,mergeRT.height,0);
        }
        if(drawTexMat==null)
        {
            drawTexMat = new Material(Shader.Find("Hidden/DrawTexture"));
        }
        drawTexMat.SetTexture("_SourceTex",mergeRT);
        drawTexMat.SetTexture("_DrawTex",texture);
        Vector4 sizeAndOffset;

        sizeAndOffset.x = size.x/maxResWidth;
        sizeAndOffset.y = size.y/maxResHeight;

        sizeAndOffset.z = position.x/maxResWidth;
        sizeAndOffset.w = position.y/maxResHeight;

        drawTexMat.SetVector("_SizeAndOffset",sizeAndOffset);
        drawTexMat.SetVector("_TilingAndOffset",tilingAndOffset);
        drawTexMat.SetColor("_Color",color);
        
        Graphics.Blit(null,tempRT,drawTexMat);

        Graphics.Blit(tempRT,mergeRT);
    }


    /// <summary>
    /// 放置UV
    /// </summary>
    /// <param name="data"></param>
    /// <param name="placed"></param>
    private void PlaceUV(SubRendererData data,List<SubRendererData> placed)
    {
        if(placed==null)
        placed = new List<SubRendererData>();

        if(data.width>maxResWidth)
        {
            Debug.LogErrorFormat("'{0}' 贴图过大!",data.name);
            return;
        }

        if(placed.Count==0)
        {
            maxResHeight = GetNewResSize(data.height);
            placed.Add(data);
            return;
        }
        int space = 4;//data.singleLineFlags?0:2;

        //是否要扩充宽度 如果高度大于宽度的两倍就进行扩充
        if(maxResHeight + data.height >maxResWidth*2.0f)
        {
            maxResWidth +=  GetNewResSize(data.width+space);
        }


        bool hasIntersect = false;

        //很笨很暴力但是很有效
        int maxCount = MAX_ITER_NUM;
        while(maxCount-->0)
        {
            hasIntersect = false;

            for(var idx=0;idx<placed.Count;idx++)
            {
                bool isIntersect;
                Vector2 dxy = placed[idx].Intersect(data,out isIntersect);
                if(!isIntersect)
                    continue;
                data.position.x += dxy.x+space;
                if(data.position.x+data.width>maxResWidth)
                {
                    data.position.x = 0;
                    data.position.y +=space;
                }
                hasIntersect = true;
                break;
                
            }
            if(!hasIntersect)
                break;
        }
        if(maxCount>=MAX_ITER_NUM)
        {
            Debug.LogError("UV 放置失败!!");
        }
        if(data.position.y + data.height>maxResHeight)
        {
            maxResHeight = GetNewResSize( data.position.y + data.height);
        }

        placed.Add(data);
    }

    public void MergeTexture(List<SubRendererData> placed,List<Vector2> mergeUV)
    {
        foreach(var data in placed)
        {
            var uv = data.uv;

            //像素空间转到UV空间 0~1，0~1
            float sw = data.width/maxResWidth;
            float sh = data.height/maxResHeight;
            //Vector2 ldPos = data.position/maxWidth;
            Vector2 ldPos = new Vector2(data.position.x/maxResWidth,data.position.y/maxResHeight);

            Vector2 ruPos = ldPos + new Vector2(sw,sh);

            //原uv数据映射到合并uv空间
            for(var idx=0;idx<uv.Count;idx++)
            {
                var uvPoint = uv[idx];
                uvPoint = Remap(uvPoint,data.minUVValue,data.maxUVValue,ldPos,ruPos);

                if(data.singleLineFlags)
                {
                    uvPoint.x = uv[idx].x;
                }
                mergeUV.Add(uvPoint);
            }
            //把图渲染上去
            var pxPos = new Vector2(data.position.x,data.position.y);
            var pxSize = new Vector2(data.width,data.height);

            
            //子图像的重复度和偏移
            Vector4 tilingAndOffset = new Vector4(1,1,0,0);
            Vector2 diff = data.maxUVValue - data.minUVValue;
            tilingAndOffset.x = diff.x;
            tilingAndOffset.y = diff.y;
            tilingAndOffset.z = data.minUVValue.x;
            tilingAndOffset.w = data.minUVValue.y;

            //让独占一行的UV铺满X
            if(data.singleLineFlags)
            {
                tilingAndOffset.z = 0;
                tilingAndOffset.x = 1;
            }
            PlaceTexture(data.mainColor,data.refMainTexture,pxPos,pxSize,tilingAndOffset);
       }

    }

    private int GetNewResSize(float value)
    {
        int res = Mathf.CeilToInt(value);

        if(HasFlags((int)flags,MeshMergeFlags.PowerOfTwo))
        {
           return Mathf.NextPowerOfTwo(res);
        }
        return res;
    }

    public static float Remap(float inputValue,float inMin,float inMax,float outMin,float outMax)
    {
        return (inputValue - inMin) * (outMax - outMin) / (inMax - inMin) + outMin;
    }
    public static Vector2 Remap(Vector2 vector, Vector2 inMin, Vector2 inMax, Vector2 outMin, Vector2 outMax)
    {
        float x = Remap(vector.x, inMin.x, inMax.x, outMin.x, outMax.x);
        float y = Remap(vector.y, inMin.y, inMax.y, outMin.y, outMax.y);
        return new Vector2(x, y);
    }

}
